/**
 * Example: http://keaplogik.blogspot.ca/2012/05/atmosphere-websockets-comet-with-spring.html
 */

package com.sian.anyun.web.controllers;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Locale;

import javax.servlet.ServletInputStream;

import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.sian.anyun.web.model.PeerMessage;
import com.sian.anyun.web.services.BroadcasterService;
import com.sian.anyun.web.services.FeedService;
import com.sian.anyun.web.services.PresentService;
import com.sian.security.web.model.Credential;
import com.sian.security.web.service.SecurityService;

/**
 * Handles requests for the application home page.
 */
@Controller
@RequestMapping("/")
public class BroadcasterController {
	private static final Logger logger = LoggerFactory.getLogger(BroadcasterController.class);

	@Autowired
	FeedService feedService;
	@Autowired
	BroadcasterService broadcasterService;
	@Autowired
	PresentService presentService;
	@Autowired
	SecurityService securityService;
	//@Autowired
	//WorkSpace workSpace;
	
	private void addEventListener(final AtmosphereResource resource) {
		resource.addEventListener(new AtmosphereResourceEventListenerAdapter() {
			@Override
			public void onDisconnect(AtmosphereResourceEvent event)
			{
				presentService.remove(event.getResource().uuid());
				broadcasterService.unsubscribeFromSystemBroadcaster(event.getResource());

				event.getResource().removeEventListener(this);
			}
			@Override
			public void onBroadcast(AtmosphereResourceEvent event) {
				super.onBroadcast(event);
				resource.suspend();
		    }
		});
	}

	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		model.addAttribute("serverTime", feedService.formatDate(locale));
		return "/home";
	}

	@RequestMapping(value = "/broadcast/subscribe")
	@ResponseBody
	public void messageAsync(AtmosphereResource atmosphereResource) {
		Credential credential = securityService.getCredential();
		if(null != credential)
		{
			if(!presentService.isRegister(credential.getJ_username()))
			{
				userPresent(credential.getJ_username(), atmosphereResource);
			}
			else
			{
				if(!atmosphereResource.uuid().equals(presentService.getAtmosphereResource(credential.getJ_username()).uuid()))
				{
					// Concurrently login.
					userUnpresent(credential.getJ_username());
					userPresent(credential.getJ_username(), atmosphereResource);
				}
				
				synchronized(credential)
				{
					logger.debug("Current login user: " + presentService.getCurrentUserId(atmosphereResource));
					try {
						ServletInputStream inputStream = atmosphereResource.getRequest().getInputStream();
						processMessage(inputStream);
					}
					catch(Exception e)
					{
						logger.debug(e.getMessage(), e);
					}
				}
			}
		}
		else
		{
			// TODO: Re-login
		}
	}
	
	protected void processMessage(ServletInputStream inputStream) throws IOException
	{
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		
		byte[] buffer = new byte[1024];
		int bytesRead;
		try {
			while ((bytesRead = inputStream.read(buffer)) != -1) {
				baos.write(buffer, 0, bytesRead);
			}
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}

		final ObjectMapper mapper = new ObjectMapper();
		if(baos.toString().length() > 0)
		{
			PeerMessage peerMessage = mapper.readValue(baos.toString(), PeerMessage.class);
			processMessage(peerMessage);
		}
	}

	protected void processMessage(PeerMessage peerMessage)
	{
		logger.debug(peerMessage.getCommand());
	}
	
	protected void userUnpresent(String userId)
	{
		AtmosphereResource previousResource = presentService.getAtmosphereResource(userId);
		broadcasterService.unsubscribeFromSystemBroadcaster(previousResource);
		presentService.remove(previousResource.uuid());
		previousResource.getResponse().destroy();
	}
	
	protected void userPresent(String userId, AtmosphereResource atmosphereResource)
	{		
		this.addEventListener(atmosphereResource);
		atmosphereResource.suspend();
		
		presentService.register(userId, atmosphereResource.uuid());
		broadcasterService.subscribeToSystemBroadcaster(atmosphereResource);

		Object missedMessage = atmosphereResource.getAtmosphereResourceEvent().getMessage();
		if(null != missedMessage)
		{
			logger.debug(missedMessage.toString());
			broadcasterService.sendMessageToPeer(userId, userId, missedMessage);
		}
	}
}
